package io.renren.modules.demo.algorithm;

import java.util.ArrayList;

public class Chromosome
{
    private ArrayList<Gene> chromosome;

    private double fitness;

    public Chromosome(ArrayList<Gene> chromosome)
    {
        this.chromosome = new ArrayList<>(chromosome);
        this.CalculateFitness();
    }

    public ArrayList<Gene> getChromosome()
    {
        return chromosome;
    }

    public void setChromosome(ArrayList<Gene> chromosome)
    {
        this.chromosome = new ArrayList<>(chromosome);
    }

    public double getFitness()
    {
        return fitness;
    }

    public void setFitness(double fitness)
    {
        this.fitness = fitness;
    }

    public Chromosome()
    {

    }

    public Chromosome(Chromosome chromosome)
    {
        this.chromosome = new ArrayList<>(chromosome.getChromosome());
        this.fitness = chromosome.getFitness();
    }

    public void Initialize()
    {
        ArrayList<Warehouse> warehouseArrayListCopy = new ArrayList<>(CarScheduling.getWarehouseArrayList());
        ArrayList<Car> carArrayListCopy = new ArrayList<>(CarScheduling.getCarArrayList());
        int carNumber = 0;  //已分配的车辆数量
        ArrayList<Gene> geneArrayList = new ArrayList<>();
        while(warehouseArrayListCopy.size() > 0)
        {
            int carRandomIndex = (int) (Math.random() * carArrayListCopy.size());
            Car car = new Car(carArrayListCopy.get(carRandomIndex));
            int warehouseRandomIndex = (int) (Math.random() * warehouseArrayListCopy.size());
            Warehouse warehouse = warehouseArrayListCopy.get(warehouseRandomIndex);
            if (warehouse.getDemand() <= car.getCarryingCapacity())
            {
                carArrayListCopy.remove(carRandomIndex);
                car.setCarryingCapacity(car.getCarryingCapacity() - warehouse.getDemand());
                geneArrayList.add(new Gene(true, warehouse.getSerialNumber()));
                warehouseArrayListCopy.remove(warehouseRandomIndex);
                ArrayList<Warehouse> temp = new ArrayList<>();
                for (int i = 0; i < warehouseArrayListCopy.size(); i++)
                {
                    if (warehouseArrayListCopy.get(i).getDemand() <= car.getCarryingCapacity())
                    {
                        car.setCarryingCapacity(car.getCarryingCapacity() - warehouseArrayListCopy.get(i).getDemand());
                        geneArrayList.add(new Gene(true, warehouseArrayListCopy.get(i).getSerialNumber()));
                        temp.add(warehouseArrayListCopy.get(i));
                    }
                }
                carNumber++;
                geneArrayList.add(new Gene(false, CarScheduling.getWarehouseArrayList().size() + car.getSerialNumber()));
                for(int i = 0; i < temp.size(); i++)
                {
                    for(int j = 0; j < warehouseArrayListCopy.size(); j++)
                    {
                        if(warehouseArrayListCopy.get(j).getSerialNumber() == temp.get(i).getSerialNumber())
                        {
                            warehouseArrayListCopy.remove(j);
                            break;
                        }
                    }
                }
            }
            if(carArrayListCopy.size() == 0)
            {
                for(int i = 0; i < warehouseArrayListCopy.size(); i++)
                {
                    geneArrayList.add(new Gene(true, warehouseArrayListCopy.get(i).getSerialNumber()));
                }
                break;
            }
        }
        if(carNumber == CarScheduling.getCarArrayList().size())
            this.chromosome = new ArrayList<>(geneArrayList);
        else if(carNumber < CarScheduling.getCarArrayList().size())
        {
            for(int i = carNumber; i < CarScheduling.getCarArrayList().size(); i++)
            {
                int carRandomIndex = (int) (Math.random() * carArrayListCopy.size());
                Car car = new Car(carArrayListCopy.get(carRandomIndex));
                geneArrayList.add(new Gene(false, CarScheduling.getWarehouseArrayList().size() + car.getSerialNumber()));
                carArrayListCopy.remove(carRandomIndex);
            }
            this.chromosome = new ArrayList<>(geneArrayList);
        }
        else
            this.chromosome = null;
    }

    public void CalculateFitness()
    {
        int cost = 0;
        int notUsedCarNumber = 0;
        ArrayList<Integer> warehouseIndex = new ArrayList<>();
        ArrayList<Integer> carIndex = new ArrayList<>();
        for(int i = 0; i < chromosome.size(); i++)
        {
            if(chromosome.get(i).isWarehouse())
                warehouseIndex.add(i);
            else
                carIndex.add(i);
        }
        boolean valid = true;
        if(carIndex.size() == 0 || warehouseIndex.size() == 0)
        {
            cost = 10000000;  //直接作废
            valid = false;
        }
        if(valid)
        {
            cost += 1000000 * warehouseIndex.get(0);  //有车被浪费了的惩罚
            if (carIndex.get(carIndex.size() - 1) > warehouseIndex.get(warehouseIndex.size() - 1))
            {
                notUsedCarNumber = carIndex.get(carIndex.size() - 1) - warehouseIndex.get(warehouseIndex.size() - 1) - 1;
            }
            else
            {
                cost += 1000000 * (warehouseIndex.get(warehouseIndex.size() - 1) - carIndex.get(carIndex.size() - 1));  //每有一个仓库没有被配送就惩罚一次
            }
            for (int i = 0; i < carIndex.size() - notUsedCarNumber; i++)
            {
                if (i + 1 < warehouseIndex.get(warehouseIndex.size() - 1) && i + 1 < carIndex.size())
                {
                    if (carIndex.get(i) + 1 == carIndex.get(i + 1))
                    {
                        cost += 1000000;  //有车被浪费了的惩罚
                    }
                }
                if (i == 0)
                {
                    Car car = new Car(CarScheduling.getCarArrayList().get(chromosome.get(carIndex.get(0)).getSerialNumber() - 1 - CarScheduling.getWarehouseArrayList().size()));
                    cost += car.getUseCost();
                    for (int j = 0; j < carIndex.get(0); j++)
                    {
                        cost += CarScheduling.CalculateDistance(chromosome.get(j), car) * car.getCostPerKilometer();
                        car.setCurrentLocation(chromosome.get(j).getSerialNumber());
                        car.setCarryingCapacity(car.getCarryingCapacity() - CarScheduling.getWarehouseArrayList().get(chromosome.get(j).getSerialNumber() - 1).getDemand());
                    }
                    cost += CarScheduling.CalculateDistance(car) * car.getCostPerKilometer();  //返程费用
                    if (car.getCarryingCapacity() < 0)
                    {
                        cost += 1000000 * Math.abs(car.getCarryingCapacity());  //超出载重的惩罚
                    }
                    else
                    {
                        cost += 10000 * car.getCarryingCapacity();  //空载惩罚
                    }
                }
                else
                {
                    Car car = new Car(CarScheduling.getCarArrayList().get(chromosome.get(carIndex.get(i)).getSerialNumber() - 1 - CarScheduling.getWarehouseArrayList().size()));
                    cost += car.getUseCost();
                    for (int j = carIndex.get(i - 1) + 1; j < carIndex.get(i); j++)
                    {
                        cost += CarScheduling.CalculateDistance(chromosome.get(j), car) * car.getCostPerKilometer();
                        car.setCurrentLocation(chromosome.get(j).getSerialNumber());
                        car.setCarryingCapacity(car.getCarryingCapacity() - CarScheduling.getWarehouseArrayList().get(chromosome.get(j).getSerialNumber() - 1).getDemand());
                    }
                    cost += CarScheduling.CalculateDistance(car) * car.getCostPerKilometer();  //返程费用
                    if (car.getCarryingCapacity() < 0)
                    {
                        cost += 1000000 * Math.abs(car.getCarryingCapacity());  //超出载重的惩罚
                    }
                    else
                    {
                        cost += 10000 * car.getCarryingCapacity();  //空载惩罚
                    }
                }
            }
        }
        this.fitness = 1.0 / cost;
    }

    @Override
    public String toString()
    {
        String schedulingList = Integer.toString(CarScheduling.getWarehouseArrayList().size());
        schedulingList += " ";
        for(int i = 0; i < chromosome.size(); i++)
        {
            if(i != chromosome.size() - 1)
            {
                if (chromosome.get(i).isWarehouse())
                {
                    schedulingList += Integer.toString(chromosome.get(i).getSerialNumber());
                    schedulingList += " ";
                    schedulingList += Integer.toString(CarScheduling.getWarehouseArrayList().get(chromosome.get(i).getSerialNumber() - 1).getLocationX());
                    schedulingList += " ";
                    schedulingList += Integer.toString(CarScheduling.getWarehouseArrayList().get(chromosome.get(i).getSerialNumber() - 1).getLocationY());
                    schedulingList += " ";
                }
                else
                {
                    schedulingList += Integer.toString(chromosome.get(i).getSerialNumber());
                    schedulingList += " ";
                }
            }
            else
            {
                if (chromosome.get(i).isWarehouse())
                {
                    schedulingList += Integer.toString(chromosome.get(i).getSerialNumber());
                    schedulingList += " ";
                    schedulingList += Integer.toString(CarScheduling.getWarehouseArrayList().get(chromosome.get(i).getSerialNumber() - 1).getLocationX());
                    schedulingList += " ";
                    schedulingList += Integer.toString(CarScheduling.getWarehouseArrayList().get(chromosome.get(i).getSerialNumber() - 1).getLocationY());
                }
                else
                {
                    schedulingList += Integer.toString(chromosome.get(i).getSerialNumber());
                }
            }
        }
        return schedulingList;
    }

    public String toString1()
    {
        String schedulingList = "";
        for(int i = 0; i < chromosome.size(); i++)
        {
            if(i != chromosome.size() - 1)
            {
                schedulingList += Integer.toString(chromosome.get(i).getSerialNumber());
                schedulingList += " ";
            }
            else
            {
                schedulingList += Integer.toString(chromosome.get(i).getSerialNumber());
            }
        }
        return schedulingList;
    }

    public int TotalCarNumberInUse()
    {
        int notUsedCarNumber = 0;
        ArrayList<Integer> warehouseIndex = new ArrayList<>();
        ArrayList<Integer> carIndex = new ArrayList<>();
        for(int i = 0; i < chromosome.size(); i++)
        {
            if(chromosome.get(i).isWarehouse())
                warehouseIndex.add(i);
            else
                carIndex.add(i);
        }
        if (carIndex.get(carIndex.size() - 1) > warehouseIndex.get(warehouseIndex.size() - 1))
        {
            notUsedCarNumber = carIndex.get(carIndex.size() - 1) - warehouseIndex.get(warehouseIndex.size() - 1) - 1;
            return carIndex.size() - notUsedCarNumber;
        }
        else
            return -1;
    }

    public int TotalCarNumberNotUse()
    {
        int notUsedCarNumber = 0;
        ArrayList<Integer> warehouseIndex = new ArrayList<>();
        ArrayList<Integer> carIndex = new ArrayList<>();
        for(int i = 0; i < chromosome.size(); i++)
        {
            if(chromosome.get(i).isWarehouse())
                warehouseIndex.add(i);
            else
                carIndex.add(i);
        }
        if (carIndex.get(carIndex.size() - 1) > warehouseIndex.get(warehouseIndex.size() - 1))
        {
            notUsedCarNumber = carIndex.get(carIndex.size() - 1) - warehouseIndex.get(warehouseIndex.size() - 1) - 1;
            return notUsedCarNumber;
        }
        else
            return 0;
    }

    public int[] TotalDistanceAndCost()
    {
        int[] distanceAndCost = new int[2];
        int distance = 0;
        int cost = 0;
        int carNumberInUse = TotalCarNumberInUse();
        if(carNumberInUse != -1)
        {
            int notUseCarNumber = TotalCarNumberNotUse();
            int preDistance = 0;
            for (int i = 0; i < chromosome.size() - notUseCarNumber; i++)
            {
                if (i == 0)
                {
                    distance += (Math.abs(CarScheduling.getWarehouseArrayList().get(chromosome.get(i).getSerialNumber() - 1).getLocationX())
                            + Math.abs(CarScheduling.getWarehouseArrayList().get(chromosome.get(i).getSerialNumber() - 1).getLocationY()));
                }
                else
                {
                    if (chromosome.get(i).isWarehouse())
                    {
                        if (!chromosome.get(i - 1).isWarehouse())
                        {
                            distance += (Math.abs(CarScheduling.getWarehouseArrayList().get(chromosome.get(i).getSerialNumber() - 1).getLocationX())
                                    + Math.abs(CarScheduling.getWarehouseArrayList().get(chromosome.get(i).getSerialNumber() - 1).getLocationY()));
                        }
                        else
                        {
                            distance += (Math.abs(CarScheduling.getWarehouseArrayList().get(chromosome.get(i).getSerialNumber() - 1).getLocationX()
                                    - CarScheduling.getWarehouseArrayList().get(chromosome.get(i - 1).getSerialNumber() - 1).getLocationX())
                                    + Math.abs(CarScheduling.getWarehouseArrayList().get(chromosome.get(i).getSerialNumber() - 1).getLocationY()
                                    - CarScheduling.getWarehouseArrayList().get(chromosome.get(i - 1).getSerialNumber() - 1).getLocationY()));
                        }
                    }
                    else
                    {
                        distance += (Math.abs(CarScheduling.getWarehouseArrayList().get(chromosome.get(i - 1).getSerialNumber() - 1).getLocationX())
                                + Math.abs(CarScheduling.getWarehouseArrayList().get(chromosome.get(i - 1).getSerialNumber() - 1).getLocationY()));
                        int partDistance = distance - preDistance;
                        cost += partDistance * CarScheduling.getCarArrayList().get(chromosome.get(i).getSerialNumber()
                                - CarScheduling.getWarehouseArrayList().size() - 1).getCostPerKilometer();
                        cost += CarScheduling.getCarArrayList().get(chromosome.get(i).getSerialNumber()
                                - CarScheduling.getWarehouseArrayList().size() - 1).getUseCost();
                        preDistance = distance;
                    }
                }
            }
        }
        else
        {
            distance = -1;
            cost = -1;
        }
        distanceAndCost[0] = distance;
        distanceAndCost[1] = cost;
        return distanceAndCost;
    }

}
